import type { GameRoomConfig } from '../data/GameRoomConfig';
import type { GameRoomPlayer } from '../data/GameRoomPlayer';
import type { PlayerRoomsInfo } from '../data/PlayerRoomsInfo';
import { GameRoom } from '../entity/GameRoom';
import { Validator } from './Validator';

/**
 * 游戏房间管理器
 */
export class GameRoomMgr {
  private static _instance: GameRoomMgr;
  protected constructor() {
    this.bindLeaveEvent();
  }

  /**
   * 获取单例实例 (Get singleton instance)
   * @returns 单例实例 (Singleton instance)
   */
  public static get instance(): GameRoomMgr {
    if (!this._instance) {
      this._instance = new this();
    }
    return this._instance;
  }

  /** 游戏房间映射 */
  private _gameRooms: Map<string, GameRoom> = new Map();
  /** 玩家记录映射 */
  private _playerRooms: Map<string, PlayerRoomsInfo> = new Map();

  /**
   * 绑定玩家<原地消失>事件
   */
  private bindLeaveEvent(): void {
    // world.onPlayerLeave(({ entity }) => {
    //   const player = this.getPlayerRooms(entity.player.userId);
    //   if (player) {
    //     for (const roomId of player.rooms) {
    //       this.removePlayerFromRoom(entity.player.userId, roomId);
    //     }
    //   }
    // });
  }

  /**
   * 创建游戏房间
   * @param config 配置
   * @returns 房间ID
   */
  public createRoom(config: GameRoomConfig): string {
    Validator.validateRoomConfig(config);
    const room = new GameRoom({ players: [], ...config });
    this._gameRooms.set(room.id, room);
    // 添加初始玩家
    if (config.players) {
      config.players.forEach((player) => {
        Validator.validatePlayer(player);
        this.addPlayerToRoom(player.userId, room.id, player.entity);
      });
    }
    return room.id;
  }

  /**
   * 销毁游戏房间
   * @param roomId 房间ID
   */
  public destroyRoom(roomId: string): void {
    Validator.validateRoomId(roomId);
    // 先清空房间
    this.clearRoom(roomId);
    const room = this._gameRooms.get(roomId);
    if (room) {
      room.destroy();
      this._gameRooms.delete(roomId);
    }
  }

  /**
   * 获取游戏房间
   * @param roomId 房间ID
   */
  private getRoom(roomId: string): GameRoom | undefined {
    return this._gameRooms.get(roomId);
  }

  /**
   * 获取玩家房间信息，如果不存在则创建一个
   * @param userId 玩家ID
   */
  private getPlayerRooms(userId: string): PlayerRoomsInfo {
    return this._playerRooms.get(userId) || { rooms: [], isInMultiRoom: false };
  }

  /**
   * 删除玩家房间信息
   * @param userId 玩家ID
   */
  private deletePlayerRooms(userId: string): void {
    this._playerRooms.delete(userId);
  }

  /**
   * 添加玩家到房间
   * @param userId 玩家ID
   * @param roomId 房间ID
   * @param entity 玩家实体
   */
  public addPlayerToRoom(
    userId: string,
    roomId: string,
    entity?: GamePlayerEntity
  ): void {
    Validator.validateUserId(userId);
    Validator.validateRoomId(roomId);
    // 获取玩家的房间
    const player = this.getPlayerRooms(userId);
    // 获取房间
    const room = this.getRoom(roomId);
    // 房间不存在
    if (!room) {
      throw new Error(`(GameRoomMgr) 房间不存在: ${roomId}`);
    }
    // 房间不允许玩家进入多个房间
    if (room.allowMultiRoom === false && player.rooms.length > 0) {
      throw new Error(
        `(GameRoomMgr) 房间不允许玩家进入多个房间: roomId: ${roomId}, userId: ${userId}`
      );
    }
    // 进入了不允许玩进入其他房间的房间
    if (player.isInMultiRoom) {
      throw new Error(
        `(GameRoomMgr) 玩家已进入了不允许进入多个房间的房间: userId: ${userId}, roomId: ${roomId}`
      );
    }
    try {
      // 添加玩家到房间
      room.joinPlayer({
        userId,
        name: entity?.player.name,
        entity: entity,
      });
      // 添加玩家到房间记录
      player.rooms.push(roomId);
      if (room.allowPlayerJoin === false) {
        player.isInMultiRoom = true;
      }
      this._playerRooms.set(userId, player);
    } catch (error) {
      // 添加玩家到房间失败
      throw new Error(
        `(GameRoomMgr) 添加玩家到房间失败: ${error}, userId: ${userId}, roomId: ${roomId}`
      );
    }
  }

  /**
   * 移除玩家从房间
   * @param userId 玩家ID
   * @param roomId 房间ID
   */
  public removePlayerFromRoom(userId: string, roomId: string): void {
    Validator.validateUserId(userId);
    Validator.validateRoomId(roomId);
    // 获取玩家的房间
    const player = this.getPlayerRooms(userId);
    // 获取房间
    const room = this.getRoom(roomId);
    // 房间不存在
    if (!room) {
      throw new Error(`(GameRoomMgr) 房间不存在: ${roomId}`);
    }
    try {
      // 移除玩家从房间
      room.leavePlayer(userId);
      // 移除玩家从房间记录
      player.rooms.splice(player.rooms.indexOf(roomId), 1);
      if (room.allowPlayerLeave === false) {
        player.isInMultiRoom = false;
      }
      this._playerRooms.set(userId, player);
    } catch (error) {
      // 移除玩家从房间失败
      throw new Error(
        `(GameRoomMgr) 移除玩家从房间失败: ${error}, userId: ${userId}, roomId: ${roomId}`
      );
    }
  }

  /**
   * 批量添加玩家到房间
   * @param players 玩家
   * @param roomId 房间ID
   */
  public addPlayersToRoom(players: GameRoomPlayer[], roomId: string): void {
    Validator.validateUserIds(players.map(p => p.userId));
    for (const player of players) {
      this.addPlayerToRoom(player.userId, roomId, player.entity);
    }
  }

  /**
   * 批量移除玩家从房间
   * @param userIds 玩家ID
   * @param roomId 房间ID
   */
  public removePlayersFromRoom(userIds: string[], roomId: string): void {
    Validator.validateUserIds(userIds);
    for (const userId of userIds) {
      this.removePlayerFromRoom(userId, roomId);
    }
  }

  /**
   * 获取房间配置
   * @param roomId 房间ID
   */
  public getRoomConfig(roomId: string): GameRoomConfig | undefined {
    Validator.validateRoomId(roomId);
    const room = this.getRoom(roomId);
    if (room) {
      return room.getConfig();
    }
  }

  /**
   * 获取所有房间ID
   */
  public getRoomIds(): string[] {
    return Array.from(this._gameRooms.keys());
  }

  /**
   * 清除玩家的所有记录
   * @param userId 玩家ID
   */
  public clearPlayer(userId: string): void {
    Validator.validateUserId(userId);
    const player = this.getPlayerRooms(userId);
    // 退出所有房间
    for (const roomId of player.rooms) {
      const room = this.getRoom(roomId);
      room?.removePlayer(userId);
    }
    this.deletePlayerRooms(userId);
  }

  /**
   * 清空房间
   * @param roomId 房间ID
   */
  public clearRoom(roomId: string): void {
    Validator.validateRoomId(roomId);
    const room = this.getRoom(roomId);
    if (room) {
      room.players.forEach(({ userId }) => {
        const player = this.getPlayerRooms(userId);
        // 移除该房间记录
        player.rooms.splice(player.rooms.indexOf(roomId), 1);
        if (room.allowPlayerLeave === false) {
          player.isInMultiRoom = false;
        }
        this._playerRooms.set(userId, player);
      });
      room.clear();
    }
  }

  /**
   * 清空
   */
  public clear(): void {
    // 销毁所有房间
    for (const room of this._gameRooms.values()) {
      room.destroy();
    }
    this._gameRooms.clear();
    // 清空玩家记录
    this._playerRooms.clear();
  }
}
